package de.tum.in.eist.im.client;

import java.util.List;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Window.ClosingEvent;
import com.google.gwt.user.client.Window.ClosingHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;

import de.tum.in.eist.im.client.GUIwidgets.UserDialog;
import de.tum.in.eist.im.shared.*;

public class EIST_IM implements EntryPoint {
	// denotes when the last Event for this user has happened (in order to fetch
	// new messages)
	long clientLastTimestamp = 0;
	long lastHeartbeat = 0;

	// the graphical user interface
	private GUI gui;
	// the current user
	private User user;
	// for communication with User instead of Window.alert()
	private UserDialog dialog;
	// the proxy server where all the action takes place
	private final ChatServiceAsync proxy = GWT.create(ChatService.class);

	/**
	 * @wbp.parser.entryPoint
	 */
	public void onModuleLoad() {
		// calendar = Calendar.getInstance();

		gui = new GUI(this);

		/*
		 * final TestUser[] testUserArray = new TestUser[10];
		 * 
		 * for (int i = 0; i < testUserArray.length; i++) { testUserArray[i] =
		 * new TestUser("" + i, "" + i, this); }
		 * 
		 * Timer auto = new Timer() { public void run() { int userNumber = (int)
		 * (Math.random()*testUserArray.length); int userAction = (int)
		 * (Math.random()*3); TestUser actual = testUserArray[userNumber];
		 * if(userAction == 0) { actual.login(); } else if(userAction == 1) {
		 * actual.sendMessage("blabiblabla"); }else if(userAction == 2) {
		 * actual.logout(); Window.alert("logoout " + userNumber); } } };
		 * auto.scheduleRepeating(2000);
		 */

		Window.addWindowClosingHandler(new ClosingHandler() {
			public void onWindowClosing(ClosingEvent event) {
				if (user != null) {
					logout();
				}
			}
		});

	}

	public void register(User userFromInput) {
		proxy.registerNewUser(userFromInput, new AsyncCallback<Boolean>() {
			public void onFailure(Throwable caught) {
				Window.alert("Error while registering. Please try again");
			}

			public void onSuccess(Boolean result) {
				if (result) {
					Window.alert("Successfully registered! You can now log in.");
				} else {
					Window.alert("Username already exists.");
				}
			}
		});

	}

	public void login(final User tryUser) {
		proxy.login(tryUser, new AsyncCallback<User>() {
			public void onFailure(Throwable caught) {
				Window.alert("Login unsuccessfull. Please try again!");
			}

			public void onSuccess(User result) {
				if (result != null) {
					user = result;
					gui.flipToChatEnvironment();
					Window.alert("Welcome, " + user.getUsername() + ".");
					updateOnlineList();
					gui.getController().getGroupsettings()
							.initGroups(user.getUserGroupList());
					pollEvents();
				} else {
					gui.getLoginDialog().flagAsInvalidInput();
				}
			}
		});
	}

	private void updateOnlineList() {
		// init the list with all uses online
		proxy.getLoggedInUsers(new AsyncCallback<List<User>>() {
			public void onFailure(Throwable caught) {
				Window.alert("Error occurred while loading online Users!");
			}

			public void onSuccess(List<User> result) {
				gui.getUserOnline().init(result);
			}
		});
	}

	public void updateMembers(String groupName) {
		proxy.getGroupMembers(groupName, new AsyncCallback<List<User>>() {
			public void onFailure(Throwable caught) {
				Window.alert("Error while loading group members");
			}

			public void onSuccess(List<User> result) {
				gui.getController().getGroupsettings().updateMembers(result);
			}
		});
	}

	public void updateGroups() {
		proxy.getGroups(user, new AsyncCallback<List<String>>() {
			public void onFailure(Throwable caught) {
				Window.alert("Error while loading groups");
			}

			public void onSuccess(List<String> result) {
				gui.getController().getGroupsettings().initGroups(result);
			}
		});
	}

	// Polls the events for the specific user. Right after it got any result
	// back from the server, it polls again!
	public void pollEvents() {
		proxy.getEventsBlocking(user, new AsyncCallback<List<AbstractEvent>>() {
			public void onFailure(Throwable caught) {
				Window.alert("Connection lost :(");
			}

			public void onSuccess(List<AbstractEvent> result) {
				// 4 types of Events:
				for (AbstractEvent event : result) {
					// a user submitted a new message
					if (event instanceof MessageEvent) {
						gui.getController()
								.getChat()
								.addMessage(((MessageEvent) event).getMessage());
						// server sends heartbeat to show it is alive
					} else if (event instanceof HeartbeatEvent) {
						lastHeartbeat = event.getTimestamp();
					} else if (event instanceof GroupEvent) {
						// user gets an invitation to join a group
						if (((GroupEvent) event).isInvite()) {
							if (Window.confirm(((GroupEvent) event).getGroup()
									.getName())) {

							}
							// if user founded a new group
						} else if (((GroupEvent) event).isFounding()) {
							gui.getController()
									.getGroupsettings()
									.addGroup(
											((GroupEvent) event).getGroup()
													.getName());
						} else {
							// if a user comes online
							if (((GroupEvent) event).isLogin()) {
								updateOnlineList();
								// if a user goes offline
							} else {
								updateOnlineList();
							}
						}
					}
					clientLastTimestamp = event.getTimestamp();
				}
				// start polling again
				pollEvents();
			}
		});
	}

	public void sendMessage(String messageText, String toGroup) {
		final Message m = new Message(System.currentTimeMillis(), messageText,
				user, toGroup);

		proxy.sendMessage(m, toGroup, new AsyncCallback<Void>() {
			public void onFailure(Throwable caught) {
				Window.alert("Sending error!");
			}

			public void onSuccess(Void result) {
			}
		});
	}

	public void getLatestMessages() {
		AsyncCallback<NewMessagesChunk> newMessagesCallback = new AsyncCallback<NewMessagesChunk>() {
			public void onFailure(Throwable caught) {
				Window.alert("Error load messages!");
			}

			public void onSuccess(NewMessagesChunk result) {
				for (Message m : result.getMessages()) {
					gui.getController().getChat().addMessage(m);
					clientLastTimestamp = m.getTimestamp();
				}
			}
		};

		proxy.getNewMessages(clientLastTimestamp, newMessagesCallback);
	}

	public void logout() {
		// This method might be called when a user closes the webpage
		if (user == null) {
			return;
		}
		proxy.logout(user, new AsyncCallback<Boolean>() {
			public void onFailure(Throwable caught) {
				Window.alert("Communication error while logging out.");
			}

			public void onSuccess(Boolean result) {
				if (result) {
					Window.alert("Goodbye.");
					user = null;
					gui.flipToLoginDialog();
				} else {
					Window.alert("You weren't logged out. Please try again.");
				}
			}
		});
	}

	public ChatServiceAsync getProxy() {
		return proxy;
	}

	public String getUsername() {
		return user.getUsername();
	}

	public void addGroup(String groupName) {
		Group newGroup = new Group(groupName, user);
		newGroup.addMember(user);
		proxy.createNewGroup(newGroup, new AsyncCallback<Boolean>() {
			public void onFailure(Throwable caught) {
				Window.alert("Connection error while trying to create a new group.");
			}

			public void onSuccess(Boolean result) {
				if (!result) {
					Window.alert("Groupname already exists");
				}
			}
		});
	}

	public void addPersonToGroup(final String groupName) {
		final String invitedUser = gui.getUserOnline().getSelection();

		proxy.invitePersonToGroup(groupName, user, invitedUser,
				new AsyncCallback<Void>() {
					public void onFailure(Throwable caught) {
						Window.alert("Connection error while trying to invite");
					}

					public void onSuccess(Void result) {
						Window.alert("You invited " + invitedUser + " to join "
								+ groupName + ".");
					}
				});
	}

	public User getUser() {
		return user;
	}

	public void acceptInvite(String groupName) {
		proxy.acceptGroupInvite(groupName, user, new AsyncCallback<Void>() {
			public void onFailure(Throwable caught) {
				Window.alert("Connection error while accepting group invite");
			}

			public void onSuccess(Void result) {

			}
		});
	}
}